﻿using System;
using System.Diagnostics;
using System.Resources;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using OpenThesaurus.Resources;
using System.Globalization;
using System.Threading;
using Windows.Phone.Speech.Recognition;
using System.Linq;
using Windows.Phone.Speech.Synthesis;
using System.IO.IsolatedStorage;
using Windows.ApplicationModel; // required for STT recognition
using Windows.Phone.Speech.VoiceCommands;

namespace OpenThesaurus
{
    public partial class App : Application
    {
        /// <summary>
        /// The API URL that responds with MIME type "application/json" - RFC 4627.
        /// @<see cref="http://www.openthesaurus.de/about/api#json"/>
        /// </summary>
        public static readonly string API_URL = "http://www.openthesaurus.de/synonyme/search?format=application/json&q=";

        /// <summary>
        /// The API parameter to get similar words.
        /// Example: similar=true
        /// </summary>
        public static readonly string API_PARAMETER_SIMILARWORDS = "&similar=true";

        /// <summary>
        /// The API parameter to get substrings.
        /// Example: substring=true
        /// </summary>
        public static readonly string API_PARAMETER_SUBSTRING = "&substring=true";

        /// <summary>
        /// The supported API version. Necessary to detect API version changes.
        /// </summary>
        public static readonly string API_VERSION = "0.2";

        /// <summary>
        /// Property to provide the availability of a German Text-To-Speech engine.
        /// </summary>
        public bool TtsAvailable { get; private set; }

        /// <summary>
        /// Property to provide the availability of a German Speech-To-Text recognizer engine.
        /// </summary>
        public bool SttAvailable { get; private set; }

        /// <summary>
        /// The speech to text object used to recognize the user's voice input.
        /// </summary>
        public SpeechRecognizerUI speechToTextControl { get; private set; }

        /// <summary>
        /// The text to speech Synthesizer object used to read the terms.
        /// </summary>
        public SpeechSynthesizer textToSpeechSynthesizer { get; private set; }

        /// <summary>
        /// The key used to store and update possible crash count.
        /// </summary>
        public static readonly string CRASH_DETECTED_KEY = "CrashCount";

        /// <summary>
        /// The isolated storage used to read/write the crash detection flag.
        /// </summary>
        IsolatedStorageSettings localSettings = IsolatedStorageSettings.ApplicationSettings;

        /// <summary>
        /// Checks the availability of a German speech to text engine.
        /// </summary>
        /// <returns></returns>
        private bool checkAvailabilityOfGermanSttEngine()
        {
            try
            {
                // check for installed text-to-speech recognizers
                if (InstalledSpeechRecognizers.All.Count > 0)
                {
                    // search for a German speech recognizer
                    var germanLanguage = (from language in InstalledSpeechRecognizers.All where language.Language == "de-DE" select language).FirstOrDefault();

                    // check if a German TTS recognizer was found
                    if (germanLanguage != null)
                    {
                        speechToTextControl = new SpeechRecognizerUI();
                        // set the German speech recognizer as default since other languages doesn't make sense for a German thesaurus
                        speechToTextControl.Recognizer.SetRecognizer(germanLanguage);
                        Debug.WriteLine("Found an installed German speech recognizer. Speech-to-Text is available");
                        return true;
                    }
                    else
                    {
                        Debug.WriteLine("No German speech recognizer found. STT won't be available.");
                    }
                }
                else
                {
                    Debug.WriteLine("No speech recognizer found at all! STT won't be available.");
                }
            }
            catch (Exception ex)
            {
                // @see http://stackoverflow.com/questions/13264903/speech-recognition-supported-languages-on-wp8
                Debug.WriteLine("Dictation grammar for remote recognition of the current language is not supported for any languages that you get this error for :-/");
                Debug.WriteLine(ex.Message);
                return false;
            }

            return false;
        }

        /// <summary>
        /// Checks the availability of a German text to speech engine.
        /// </summary>
        /// <returns></returns>
        private bool checkAvailabilityOfGermanTtsEngine()
        {
            try
            {
                // check for installed speech to text engines
                if (InstalledVoices.All.Count > 0)
                {
                    // search for a German engine
                    var germanLanguage = (from language in InstalledVoices.All where language.Language == "de-DE" select language).FirstOrDefault();

                    // check if a German TTS speech to text engine was found
                    if (germanLanguage != null)
                    {
                        textToSpeechSynthesizer = new SpeechSynthesizer();
                        // set the German text to speech engine as default since other languages doesn't make sense for a German thesaurus
                        textToSpeechSynthesizer.SetVoice(germanLanguage);
                        Debug.WriteLine("Found an installed German speech to text synthesizer. Text-to-Speech is available");
                        return true;
                    }
                    else
                    {
                        Debug.WriteLine("No German speech to text synthesizer found. TTS won't be available.");
                    }
                }
                else
                {
                    Debug.WriteLine("No speech to text synthesizer found at all! TTS won't be available.");
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
                return false;
            }

            return false;
        }

        /// <summary>
        /// Bietet einen einfachen Zugriff auf den Stammframe der Phone-Anwendung.
        /// </summary>
        /// <returns>Der Stammframe der Phone-Anwendung.</returns>
        public static PhoneApplicationFrame RootFrame { get; private set; }

        /// <summary>
        /// Konstruktor für das Application-Objekt.
        /// </summary>
        public App()
        {
            // Globaler Handler für nicht abgefangene Ausnahmen.
            UnhandledException += Application_UnhandledException;

            // Standard-XAML-Initialisierung
            InitializeComponent();

            // Phone-spezifische Initialisierung
            InitializePhoneApplication();

            // Initialisierung der Sprachanzeige
            InitializeLanguage();

            // Während des Debuggens Profilerstellungsinformationen zur Grafikleistung anzeigen.
            if (Debugger.IsAttached)
            {
                // Zähler für die aktuelle Bildrate anzeigen
                Application.Current.Host.Settings.EnableFrameRateCounter = false;

                // Bereiche der Anwendung hervorheben, die mit jedem Bild neu gezeichnet werden.
                //Application.Current.Host.Settings.EnableRedrawRegions = true;

                // Nicht produktiven Visualisierungsmodus für die Analyse aktivieren,
                // in dem Bereiche einer Seite angezeigt werden, die mit einer Farbüberlagerung an die GPU übergeben wurden.
                //Application.Current.Host.Settings.EnableCacheVisualization = true;

                // Verhindert, dass der Bildschirm im Debugger ausgeschaltet wird, indem
                // die Leerlauferkennung der Anwendung deaktiviert wird.
                // Vorsicht: Nur im Debugmodus verwenden. Eine Anwendung mit deaktivierter Benutzerleerlauferkennung wird weiterhin ausgeführt
                // und verbraucht auch dann Akkuenergie, wenn der Benutzer das Handy nicht verwendet.
                PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
            }
        }

        // Code, der beim Starten der Anwendung ausgeführt werden soll (z. B. über "Start")
        // Dieser Code wird beim Reaktivieren der Anwendung nicht ausgeführt
        private void Application_Launching(object sender, LaunchingEventArgs e)
        {
            if (previousCrashDueToOsBugDetected())
            {
                SttAvailable = false; 
            }
            else
            {
                SttAvailable = checkAvailabilityOfGermanSttEngine();
            }

            TtsAvailable = checkAvailabilityOfGermanTtsEngine();

            registerVoiceCommands();
        }

        /// <summary>
        /// Checks for a previous crash caused by the STT bug in WP 8.1 on large screen devices. 
        /// "Error HRESULT E_FAIL has been returned from a call to a COM component"
        /// @see https://social.msdn.microsoft.com/Forums/windowsapps/en-US/22bd9d86-c337-495f-9b62-b8f6d68cf93b/lumia-1520-error-hresult-efail-has-been-returned-from-a-call-to-a-com-component?forum=wpdevelop
        /// </summary>
        /// <returns>true if a previous crash was detected. false if no previous crash was detected.</returns>
        private bool previousCrashDueToOsBugDetected()
        {
            if (localSettings.Contains(CRASH_DETECTED_KEY))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Registers the voice commands defined in VoiceCommands.xml.
        /// @see https://view.officeapps.live.com/op/view.aspx?src=http%3a%2f%2fvideo.ch9.ms%2fsessions%2fbuild%2f2014%2f2-530.pptx.
        /// </summary>
        private async void registerVoiceCommands()
        {
            try
            { 
                Uri uriVoiceCommands = new Uri("ms-appx:///VoiceCommands.xml", UriKind.Absolute);
                await VoiceCommandService.InstallCommandSetsFromFileAsync(uriVoiceCommands);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        // Code, der ausgeführt werden soll, wenn die Anwendung aktiviert wird (in den Vordergrund gebracht wird)
        // Dieser Code wird beim ersten Starten der Anwendung nicht ausgeführt
        private void Application_Activated(object sender, ActivatedEventArgs e)
        {
        }

        // Code, der ausgeführt werden soll, wenn die Anwendung deaktiviert wird (in den Hintergrund gebracht wird)
        // Dieser Code wird beim Schließen der Anwendung nicht ausgeführt
        private void Application_Deactivated(object sender, DeactivatedEventArgs e)
        {
        }

        // Code, der beim Schließen der Anwendung ausgeführt wird (z. B. wenn der Benutzer auf "Zurück" klickt)
        // Dieser Code wird beim Deaktivieren der Anwendung nicht ausgeführt
        private void Application_Closing(object sender, ClosingEventArgs e)
        {
            // Sicherstellen, dass der erforderliche Anwendungszustand hier beibehalten wird
        }

        // Code, der bei einem Navigationsfehler ausgeführt wird
        private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            if (Debugger.IsAttached)
            {
                // Navigationsfehler. Unterbrechen und Debugger öffnen
                Debugger.Break();
            }
        }

        // Code, der bei nicht behandelten Ausnahmen ausgeführt wird
        private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
        {
            if (e.ExceptionObject.Message.Equals("Error HRESULT E_FAIL has been returned from a call to a COM component."))
            {
                Debug.WriteLine("Detected speech-to-text OS bug. Storing a flag to tell the app at next startup to skip the STT initialization to stay stable.");

                if (localSettings.Contains(CRASH_DETECTED_KEY))
                {
                    localSettings[CRASH_DETECTED_KEY] = "crash detected";
                }
                else
                {
                    localSettings.Add(CRASH_DETECTED_KEY, "crash detected");
                }

                localSettings.Save();
            }

            if (Debugger.IsAttached)
            {
                // Eine nicht behandelte Ausnahme ist aufgetreten. Unterbrechen und Debugger öffnen
                Debugger.Break();
            }
        }

        #region Initialisierung der Phone-Anwendung

        // Doppelte Initialisierung vermeiden
        private bool phoneApplicationInitialized = false;

        // Fügen Sie keinen zusätzlichen Code zu dieser Methode hinzu
        private void InitializePhoneApplication()
        {
            if (phoneApplicationInitialized)
                return;

            // Frame erstellen, aber noch nicht als RootVisual festlegen. Dadurch kann der Begrüßungsbildschirm
            // aktiv bleiben, bis die Anwendung bereit für das Rendern ist.
            RootFrame = new PhoneApplicationFrame();
            RootFrame.Navigated += CompleteInitializePhoneApplication;

            // Navigationsfehler behandeln
            RootFrame.NavigationFailed += RootFrame_NavigationFailed;

            // Behandeln Sie Rücksetzanforderungen zum Löschen des Backstack
            RootFrame.Navigated += CheckForResetNavigation;

            // Sicherstellen, dass keine erneute Initialisierung erfolgt
            phoneApplicationInitialized = true;
        }

        // Fügen Sie keinen zusätzlichen Code zu dieser Methode hinzu
        private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
        {
            // Visuelle Stammkomponente festlegen, sodass die Anwendung gerendert werden kann
            if (RootVisual != RootFrame)
                RootVisual = RootFrame;

            // Dieser Handler wird nicht mehr benötigt und kann entfernt werden
            RootFrame.Navigated -= CompleteInitializePhoneApplication;
        }

        private void CheckForResetNavigation(object sender, NavigationEventArgs e)
        {
            // Wenn die App eine 'Reset'-Navigation empfangen hat, müssen wir prüfen
            // bei der nächsten Navigation, um festzustellen, ob der Seitenstapel zurückgesetzt werden muss
            if (e.NavigationMode == NavigationMode.Reset)
                RootFrame.Navigated += ClearBackStackAfterReset;
        }

        private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
        {
            // Heben Sie die Registrierung des Ereignisses auf, damit es nicht erneut aufgerufen wird.
            RootFrame.Navigated -= ClearBackStackAfterReset;

            // Löschen Sie den Stapel nur bei den Navigationen "neu" (vorwärts) und "Aktualisieren"
            if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
                return;

            // Löschen Sie zur Sicherstellung der UI-Konsistenz den gesamten Seitenstapel
            while (RootFrame.RemoveBackEntry() != null)
            {
                ; // unternehmen Sie nichts
            }
        }

        #endregion

        // Initialisieren Sie die Schriftart und Flussrichtung der App wie in den lokalisierten Ressourcenzeichenfolgen angegeben.
        //
        // Um sicherzustellen, dass die Schriftart der Anwendung mit den unterstützten Sprachen abgestimmt ist und dass
        // FlowDirection der einzelnen Sprachen der herkömmlichen Richtung folgt, müssen ResourceLanguage
        // und ResourceFlowDirection in jeder RESX-Datei initialisiert werden, damit sie den Werten
        // der Kultur dieser Datei entsprechen. Zum Beispiel:
        //
        // AppResources.es-ES.resx
        //    Der Wert von ResourceLanguage muss "es-ES" sein.
        //    Der Wert von ResourceFlowDirection muss "LeftToRight" sein.
        //
        // AppResources.ar-SA.resx
        //     Der Wert von ResourceLanguage muss "ar-SA" sein.
        //     Der Wert von ResourceFlowDirection muss "RightToLeft" sein.
        //
        // Weitere Informationen über die Lokalisierung von Windows Phone-Apps finden Sie unter http://go.microsoft.com/fwlink/?LinkId=262072.
        //
        private void InitializeLanguage()
        {
            // Debug output
            if (Debugger.IsAttached)
            {
                System.Diagnostics.Debug.WriteLine(
                    "************************************* \nCultureInfo.CurrentCulture.Name = " + CultureInfo.CurrentCulture.Name + ",\n" +
                    "CultureInfo.CurrentCulture.CompareInfo = " + CultureInfo.CurrentCulture.CompareInfo + ",\n" +
                    "CultureInfo.CurrentCulture.DisplayName = " + CultureInfo.CurrentCulture.DisplayName + ",\n" +
                    "CultureInfo.CurrentCulture.EnglishName = " + CultureInfo.CurrentCulture.EnglishName + ",\n" +
                    "CultureInfo.CurrentCulture.Name = " + CultureInfo.CurrentCulture.Name + ",\n" +
                    "CultureInfo.CurrentCulture.NativeName = " + CultureInfo.CurrentCulture.NativeName + ",\n" +
                    "CultureInfo.CurrentCulture.TextInfo = " + CultureInfo.CurrentCulture.TextInfo
                );

                Debug.WriteLine("\r\nCultureInfo.CurrentUICulture.Name: " + CultureInfo.CurrentUICulture.Name + "\r\n");
            }

            try
            {
                // Legen Sie die Schriftart so fest, dass sie der Anzeigesprache entspricht, die
                // in der ResourceLanguage-Ressourcenzeichenfolge der einzelnen unterstützten Sprachen definiert ist.
                //
                // Greifen Sie auf die Schriftart der neutralen Sprache zurück, wenn die
                // Anzeigesprache des Telefons nicht unterstützt wird.
                //
                // Wenn ein Compilerfehler auftritt, fehlt ResourceLanguage in
                // der Ressourcendatei.
                RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage);

                // Legen Sie FlowDirection aller Elemente im Stammframe fest und zwar auf Grundlage
                // der ResourceFlowDirection-Ressourcenzeichenfolge der einzelnen
                // unterstützten Sprachen.
                //
                // Wenn ein Compilerfehler auftritt, fehlt ResourceFlowDirection in
                // der Ressourcendatei.
                FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection);
                RootFrame.FlowDirection = flow;
            }
            catch
            {
                // Tritt hier eine Ausnahme auf, liegt das wahrscheinlich entweder an
                // ResourceLangauge ist nicht richtig auf eine unterstützte Sprache eingestellt
                // Code oder ResourceFlowDirection ist auf einen anderen Wert als LeftToRight festgelegt
                // oder RightToLeft.

                if (Debugger.IsAttached)
                {
                    Debugger.Break();
                }

                throw;
            }
        }
    }
}